package main

import (
	"fmt"
	"sync"
	"time"
)

/*
*
RWMutex 的结构体：
读写互斥锁在Go语言中的实现是RWMutex 其中不仅包括一个互斥锁，还持有两个信号量，一个用于写操作，一个用于读操作。

	type RWMutex struct {
	    w           Mutex
	    writerSem   uint32
	    readerSem   uint32
	    readerCount int32   //当前正在执行的读操作的数量
	    readerWait  int32   //当写操作被阻塞时等待的读操作个数
	}

readerCount 存储了当前正在执行的读操作的数量，最后的readerWait 表示当写操作被阻塞时等待的读操作个数。

读写锁上的方法：
读写哟是针对读写操作的互斥锁（简单理解就是读和写无法同时进行，而且根据我们的知识知道，写写也不能同时；只有读读是可以同时的），
读写锁与互斥锁的最大不同就是可以分别读写，先进行写锁定，一般用在大量读操作，少量写操作的情况；

func (rw *RWMutex) Lock()  //写锁定
func (rw *RWMutex) Unlock() //写解锁
func (rw *RWMutex) RLock()  //读锁定
func (rw *RWMutex) RUnlock() //读解锁

不要混用锁定和解锁，如：Lock 和 RUnlock、RLock 和 Unlock。因为对未读锁定的读写锁进行读解锁或对未写锁定的读写锁进行写解锁将会引起运行时错误。

如何理解读写锁呢？

	同时只能有一个 goroutine 能够获得写锁定。
	同时可以有任意多个 goroutine 获得读锁定。
	同时只能存在写锁定或读锁定（读和写互斥）。

也就是说，当有一个 goroutine 获得写锁定，其它无论是读锁定还是写锁定都将阻塞直到写解锁；当有一个 goroutine 获得读锁定，、
其它读锁定仍然可以继续；当有一个或任意多个读锁定，写锁定将等待所有读锁定解锁之后才能够进行写锁定。
所以说这里的读锁定（RLock）目的其实是告诉写锁定：有很多人正在读取数据，需等它们读（读解锁）完再来写（写锁定）。
97
*/

var (
	x      int64
	wg     sync.WaitGroup
	lock   sync.Mutex
	rwlock sync.RWMutex
)

func write() {
	// lock.Lock()   // 加互斥锁
	rwlock.Lock() // 加写锁
	x = x + 1
	time.Sleep(10 * time.Millisecond) // 假设读操作耗时10毫秒
	rwlock.Unlock()                   // 解写锁
	// lock.Unlock()                     // 解互斥锁
	wg.Done()
}

func read() {
	// lock.Lock()                  // 加互斥锁
	rwlock.RLock()               // 加读锁
	time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒
	rwlock.RUnlock()             // 解读锁
	// lock.Unlock()                // 解互斥锁
	wg.Done()
}

func main() {
	start := time.Now()
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go write()
	}

	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go read()
	}

	wg.Wait()
	end := time.Now()
	fmt.Println(end.Sub(start))
}

/**
一个要点：
需要注意的是读写锁非常适合读多写少的成还能，如果读和写的操作差别不大，，读写锁的优势也就发挥不出来，读写锁的优势就是不限制大家同时读！！！这比顺序读快多了
*/
